package uk.co.revisual.creation {
	import flash.utils.describeType;

	public class ClassFactory implements DynamicClassFactory {
		
		private var classes : Array;
				
		/** 
		 * @param item an item, or items to be added to the factory
		 * 
		 */
		public function ClassFactory( item:Object = null) : void {
			if ( item != null )addClass( item );
		}
		
		/**
		 * The factory method.
		 * @param data the classpath from which to get its class definition.
		 * @param asClass whether the object returned should be an instance or a Class referance.
		 * @return the requested item.
		 * 
		 */
		public final function create( data:Object = null, asClass:Boolean = true ):Object {
			// check to see that there are items inthe factory
			if(!classes || !classes.length) throw new FactoryError( FactoryError.EMPTY_FACTORY );
			for each (var classBag:ClassBag in classes ){				
				if( classBag.equals( data ) ){
					if(asClass)return classBag.payload;
					else return new classBag.payload;
				} 
			}
			throw new FactoryError( FactoryError.CLASSES_NOT_DECLARED, [data.toString()] );	
		}
		
		/**
		 * Adds one or more classes to the factory 
		 * @param item item to add, either a Class, or an Array of Classes
		 * @return true if all items were added successfully, or false of one or more of the items was rejected
		 */
		public function addClass( item:Object ):Boolean{
			var incomingClasses:Array;
			if( classes == null ) classes = [];
			// deal with item type
			if( item is Class ) incomingClasses = [ item ];
			else if( item is Array ) incomingClasses = item as Array;			
			else return false;
			
			var newClassesToAdd:Array = filterClassesForDuplicates( incomingClasses );
			classes = classes.concat( newClassesToAdd );
			return ( incomingClasses.length == newClassesToAdd.length );
		}
		
		/**
		 * Determines if a class is contained in this factory.
		 * @param classPath The classpath to test
		 * @return The test result.
		 */
		public function hasClass( item:Object ):Boolean{
			for each (var classBag:ClassBag in classes ){				
				if( classBag.equals( item ) ) return true;
			}
			return false;
		}

		
		/**
		 * @private
		 */
		private function filterClassesForDuplicates( classes:Array ):Array{
			var a:Array = [];
			for each (var c:Class in classes ){		
				if( !hasClass( c ) ) 
					a.push( new ClassBag( c ) );
			}
			return a;
		}
		
	}
}
import flash.utils.describeType;

/**
 * Wrapper class for a Class referance.
 */
internal class ClassBag{
	
	/**
	 * the package of the wrapped Class 
	 */
	public var pkg:String;
	
	/**
	 * the name of the wrapped Class
	 */
	public var name:String;
	
	/**
	 * the Class referance 
	 */
	internal var payload:Class;
	
	
	/** 
	 * @param c the Class to be wrapped
	 */
	public function ClassBag ( c:Class ):void{
		payload = c;
		describeClass( c );
	}
	
	public function toString():String{
		return pkg + "." + name;
	}
	
	/**
	 * Evaluates the equality of the value passed with the wrapped Class.
	 * can pass the full qualified class name ( my.full.package.Class or my.full.package::Class )
	 * or an instance of the Class ref itself
	 * @param value item to evaluate against
	 * @return the result
	 * 
	 */
	public function equals( value:Object ):Boolean{
		return ( 	( value.toString() == pkg + "." + name ) 	||
					( value.toString() == pkg + "::" + name ) 	||
					( value == payload )						);
	}
	
	/**
	 * @private
	 */
	private function describeClass( c:Class ):void{
		var description:XML = describeType( c );
		var split:Array = description.@name.toString().split("::");
		pkg = String( split[0] )
		name = String( split[1] );
	}
}
